/**
 * @file defines echarts Heatmap Chart
 * @author Ovilia (me@zhangwenli.com)
 * Inspired by https://github.com/mourner/simpleheat
 *
 * @module
 */


var defaultOptions = {
    blurSize: 30,
    // gradientColors is either shaped of ['blue', 'cyan', 'lime', 'yellow', 'red']
    // or [{
    //    offset: 0.2,
    //    color: 'blue'
    // }, {
    //    offset 0.8,
    //    color: 'cyan'
    // }]
    gradientColors: [
        'blue',
        'cyan',
        'lime',
        'yellow',
        'red'
    ],
    minAlpha: 0.05,
    valueScale: 1,
    opacity: 1
};
var BRUSH_SIZE = 20;
var GRADIENT_LEVELS = 256;
/**
     * Heatmap Chart
     *
     * @class
     * @param {Object} opt options
     */
function Heatmap(opt) {
    this.option = opt;
    if (opt) {
        for (var i in defaultOptions) {
            if (opt[i] !== undefined) {
                this.option[i] = opt[i];
            } else {
                this.option[i] = defaultOptions[i];
            }
        }
    } else {
        this.option = defaultOptions;
    }
}
Heatmap.prototype = {
    /**
         * Renders Heatmap and returns the rendered canvas
         * @param {Array} [x, y, value] array of data
         * @param {number} canvas width
         * @param {number} canvas height
         * @return {Object} rendered canvas
         */
    getCanvas: function (data, width, height) {
        var brush = this._getBrush();
        var gradient = this._getGradient();
        var r = BRUSH_SIZE + this.option.blurSize;
        var canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        var ctx = canvas.getContext('2d');
        var len = data.length;
        for (var i = 0; i < len; ++i) {
            var p = data[i];
            var x = p[0];
            var y = p[1];
            var value = p[2];
            // calculate alpha using value
            var alpha = Math.min(1, Math.max(value * this.option.valueScale || this.option.minAlpha, this.option.minAlpha));
            // draw with the circle brush with alpha
            ctx.globalAlpha = alpha;
            ctx.drawImage(brush, x - r, y - r);
        }
        // colorize the canvas using alpha value and set with gradient
        var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        var pixels = imageData.data;
        var len = pixels.length / 4;
        while (len--) {
            var id = len * 4 + 3;
            var alpha = pixels[id] / 256;
            var colorOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1));
            pixels[id - 3] = gradient[colorOffset * 4];
            // red
            pixels[id - 2] = gradient[colorOffset * 4 + 1];
            // green
            pixels[id - 1] = gradient[colorOffset * 4 + 2];
            // blue
            pixels[id] *= this.option.opacity;    // alpha
        }
        ctx.putImageData(imageData, 0, 0);
        return canvas;
    },
    /**
         * get canvas of a black circle brush used for canvas to draw later
         * @private
         * @returns {Object} circle brush canvas
         */
    _getBrush: function () {
        if (!this._brushCanvas) {
            this._brushCanvas = document.createElement('canvas');
            // set brush size
            var r = BRUSH_SIZE + this.option.blurSize;
            var d = r * 2;
            this._brushCanvas.width = d;
            this._brushCanvas.height = d;
            var ctx = this._brushCanvas.getContext('2d');
            // in order to render shadow without the distinct circle,
            // draw the distinct circle in an invisible place,
            // and use shadowOffset to draw shadow in the center of the canvas
            ctx.shadowOffsetX = d;
            ctx.shadowBlur = this.option.blurSize;
            // draw the shadow in black, and use alpha and shadow blur to generate
            // color in color map
            ctx.shadowColor = 'black';
            // draw circle in the left to the canvas
            ctx.beginPath();
            ctx.arc(-r, r, BRUSH_SIZE, 0, Math.PI * 2, true);
            ctx.closePath();
            ctx.fill();
        }
        return this._brushCanvas;
    },
    /**
         * get gradient color map
         * @private
         * @returns {array} gradient color pixels
         */
    _getGradient: function () {
        if (!this._gradientPixels) {
            var levels = GRADIENT_LEVELS;
            var canvas = document.createElement('canvas');
            canvas.width = 1;
            canvas.height = levels;
            var ctx = canvas.getContext('2d');
            // add color to gradient stops
            var gradient = ctx.createLinearGradient(0, 0, 0, levels);
            var len = this.option.gradientColors.length;
            for (var i = 0; i < len; ++i) {
                if (typeof this.option.gradientColors[i] === 'string') {
                    gradient.addColorStop((i + 1) / len, this.option.gradientColors[i]);
                } else {
                    gradient.addColorStop(this.option.gradientColors[i].offset, this.option.gradientColors[i].color);
                }
            }
            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, 1, levels);
            this._gradientPixels = ctx.getImageData(0, 0, 1, levels).data;
        }
        return this._gradientPixels;
    }
};
module.exports = Heatmap || module.exports;;